001 /*
002 * Copyright 2004-2006 Stephen J. McConnell.
003 * Copyright 1999-2004 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package net.dpml.lang;
018
019 import java.io.Serializable;
020
021 import java.util.StringTokenizer;
022
023 /**
024 * This class is used to hold version information.
025 * <p />
026 *
027 * The version number is made up of three dot-separated fields:
028 * <p />
029 * "<b>major.minor.micro</b>"
030 * <p />
031 * The <b>major</b>, <b>minor</b> and <b>micro</b> fields are
032 * <i>integer</i> numbers represented in decimal notation and have the
033 * following meaning:
034 * <ul>
035 *
036 * <p /><li><b>major</b> - When the major version changes (in ex. from
037 * "1.5.12" to "2.0.0"), then backward compatibility
038 * with previous releases is not granted.</li><p />
039 *
040 * <p /><li><b>minor</b> - When the minor version changes (in ex. from
041 * "1.5.12" to "1.6.0"), then backward compatibility
042 * with previous releases is granted, but something changed in the
043 * implementation of the Component. (ie it methods could have been added)</li><p />
044 *
045 * <p /><li><b>micro</b> - When the micro version changes (in ex.
046 * from "1.5.12" to "1.5.13"), then the the changes are
047 * small forward compatible bug fixes or documentation modifications etc.
048 * </li>
049 * </ul>
050 *
051 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
052 * @version 1.0.2
053 */
054 public final class Version implements Comparable, Serializable
055 {
056 /**
057 * Version -1.0.0.
058 */
059 public static final Version NULL_VERSION = new Version( -1, 0, 0 );
060
061 /**
062 * Serial version identifier.
063 */
064 static final long serialVersionUID = 1L;
065
066 private int m_major;
067 private int m_minor;
068 private int m_micro;
069
070 /**
071 * Parse a version out of a string.
072 * The version string format is <major>.<minor>.<micro> where
073 * both minor and micro are optional.
074 *
075 * @param version The input version string
076 * @return the new Version object
077 * @throws NumberFormatException if an error occurs
078 * @throws IllegalArgumentException if an error occurs
079 * @throws NullPointerException if the version argument is <code>null</code>.
080 * @since 4.1
081 */
082 public static Version parse( final String version )
083 throws NumberFormatException, IllegalArgumentException, NullPointerException
084 {
085 if( version == null )
086 {
087 throw new NullPointerException( "version" );
088 }
089
090 final StringTokenizer tokenizer = new StringTokenizer( version, "." );
091 final String[] levels = new String[ tokenizer.countTokens() ];
092 for( int i = 0; i < levels.length; i++ )
093 {
094 levels[ i ] = tokenizer.nextToken();
095 }
096
097 int major = -1;
098 if( 0 < levels.length )
099 {
100 major = Integer.parseInt( levels[ 0 ] );
101 }
102
103 int minor = 0;
104 if( 1 < levels.length )
105 {
106 minor = Integer.parseInt( levels[ 1 ] );
107 }
108
109 int micro = 0;
110 if( 2 < levels.length )
111 {
112 micro = Integer.parseInt( levels[ 2 ] );
113 }
114
115 return new Version( major, minor, micro );
116 }
117
118 /**
119 * Create a new instance of a <code>Version</code> object with the
120 * specified version numbers.
121 *
122 * @param major This <code>Version</code> major number.
123 * @param minor This <code>Version</code> minor number.
124 * @param micro This <code>Version</code> micro number.
125 */
126 public Version( final int major, final int minor, final int micro )
127 {
128 m_major = major;
129 m_minor = minor;
130 m_micro = micro;
131 }
132
133 /**
134 * Retrieve major component of version.
135 *
136 * @return the major component of version
137 * @since 4.1
138 */
139 public int getMajor()
140 {
141 return m_major;
142 }
143
144 /**
145 * Retrieve minor component of version.
146 *
147 * @return the minor component of version
148 * @since 4.1
149 */
150 public int getMinor()
151 {
152 return m_minor;
153 }
154
155 /**
156 * Retrieve micro component of version.
157 *
158 * @return the micro component of version.
159 * @since 4.1
160 */
161 public int getMicro()
162 {
163 return m_micro;
164 }
165
166 /**
167 * Check this <code>Version</code> against another for equality.
168 * <p />
169 * If this <code>Version</code> is compatible with the specified one, then
170 * <b>true</b> is returned, otherwise <b>false</b>.
171 *
172 * @param other The other <code>Version</code> object to be compared with this
173 * for equality.
174 * @return <b>true</b> if this <code>Version</code> is compatible with the specified one
175 * @since 4.1
176 */
177 public boolean equals( final Version other )
178 {
179 if( other == null )
180 {
181 return false;
182 }
183 boolean isEqual = ( getMajor() == other.getMajor() );
184 if( isEqual )
185 {
186 isEqual = ( getMinor() == other.getMinor() );
187 }
188 if( isEqual )
189 {
190 isEqual = ( getMicro() == other.getMicro() );
191 }
192 return isEqual;
193 }
194
195 /**
196 * Indicates whether some other object is "equal to" this <code>Version</code>.
197 * Returns <b>true</b> if the other object is an instance of <code>Version</code>
198 * and has the same major, minor, and micro components.
199 *
200 * @param other an <code>Object</code> value
201 * @return <b>true</b> if the other object is equal to this <code>Version</code>
202 */
203 public boolean equals( final Object other )
204 {
205 boolean isEqual = false;
206 if( other instanceof Version )
207 {
208 isEqual = equals( (Version) other );
209 }
210 return isEqual;
211 }
212
213 /**
214 * Add a hashing function to ensure the Version object is
215 * treated as expected in hashmaps and sets. NOTE: any
216 * time the equals() is overridden, hashCode() should also
217 * be overridden.
218 *
219 * @return the hashCode
220 */
221 public int hashCode()
222 {
223 int hash = 61486123 * getMajor();
224 hash = hash + 1273621 * getMinor();
225 hash = hash + 8912738 * getMicro();
226 return hash;
227 }
228
229 /**
230 * Check this <code>Version</code> against another for compliancy
231 * (compatibility).
232 * <p />
233 * If this <code>Version</code> is compatible with the specified one, then
234 * <b>true</b> is returned, otherwise <b>false</b>. Be careful when using
235 * this method since, in example, version 1.3.7 is compliant to version
236 * 1.3.6, while the opposite is not.
237 * <p />
238 * The following example displays the expected behaviour and results of version.
239 * <pre>
240 * final Version v1 = new Version( 1, 3, 6 );
241 * final Version v2 = new Version( 1, 3, 7 );
242 * final Version v3 = new Version( 1, 4, 0 );
243 * final Version v4 = new Version( 2, 0, 1 );
244 *
245 * assert( v1.complies( v1 ) );
246 * assert( ! v1.complies( v2 ) );
247 * assert( v2.complies( v1 ) );
248 * assert( ! v1.complies( v3 ) );
249 * assert( v3.complies( v1 ) );
250 * assert( ! v1.complies( v4 ) );
251 * assert( ! v4.complies( v1 ) );
252 * </pre>
253 *
254 * @param other The other <code>Version</code> object to be compared with this
255 * for compliancy (compatibility).
256 * @return <b>true</b> if this <code>Version</code> is compatible with the specified one
257 */
258 public boolean complies( final Version other )
259 {
260 if( other == null )
261 {
262 return false;
263 }
264 if( other.m_major == -1 )
265 {
266 return true;
267 }
268 if( m_major != other.m_major )
269 {
270 return false;
271 }
272 else if( m_minor < other.m_minor )
273 {
274 //If of major version but lower minor version then incompatible
275 return false;
276 }
277 else
278 {
279 //If same major version, same minor version but lower micro level
280 //then incompatible
281 return !( m_minor == other.m_minor && m_micro < other.m_micro );
282 }
283 }
284
285 /**
286 * Overload toString to report version correctly.
287 *
288 * @return the dot seperated version string
289 */
290 public String toString()
291 {
292 return m_major + "." + m_minor + "." + m_micro;
293 }
294
295 /**
296 * Compare two versions together according to the
297 * {@link Comparable} interface.
298 * @param o the other object
299 * @return number indicating relative value (-1, 0, 1)
300 * @throws NullPointerException if the argument is null.
301 */
302 public int compareTo( Object o )
303 throws NullPointerException
304 {
305 if( o == null )
306 {
307 throw new NullPointerException ( "o" );
308 }
309
310 Version other = (Version) o;
311
312 if( getMajor() < other.getMajor() )
313 {
314 return -1;
315 }
316
317 if( getMajor() > other.getMajor() )
318 {
319 return 1;
320 }
321
322 if( getMinor() < other.getMinor() )
323 {
324 return -1;
325 }
326
327 if( getMinor() > other.getMinor() )
328 {
329 return 1;
330 }
331
332 if( getMicro() < other.getMicro() )
333 {
334 return -1;
335 }
336
337 if( getMicro() > other.getMicro() )
338 {
339 return 1;
340 }
341
342 return 0;
343 }
344 }